package com.jyniubi.gmail.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.jyniubi.gmail.pms.entity.AttrEntity;
import com.jyniubi.gmail.pms.entity.BrandEntity;
import com.jyniubi.gmail.pms.entity.CategoryEntity;
import com.jyniubi.gmail.search.pojo.Goods;
import com.jyniubi.gmail.search.pojo.SearchRequestVo;
import com.jyniubi.gmail.search.pojo.SearchResponseAttrVo;
import com.jyniubi.gmail.search.pojo.SearchResponseVo;
import com.jyniubi.gmail.search.service.SearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("SearchService")
public class SearchServiceImpl implements SearchService {

    @Resource
    private RestHighLevelClient restHighLevelClient;
    @Override
    public SearchResponseVo search(SearchRequestVo requestVo) {
        try {
            SearchResponse response = this.restHighLevelClient.search(new SearchRequest(new String[]{"goods"}, this.buildDsl(requestVo)), RequestOptions.DEFAULT);
            // 解析结果集
            SearchResponseVo responseVo = this.parseResult(response);
            responseVo.setPageNum(requestVo.getPageNum());
            responseVo.setPageSize(requestVo.getPageSize());
            return responseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private SearchResponseVo parseResult(SearchResponse response){
        SearchResponseVo responseVo = new SearchResponseVo();
        // 1.解析普通的搜索结果集
        SearchHits hits = response.getHits();
        // 总记录数
        responseVo.setTotal(hits.getTotalHits().value);
        // 获取当前页的数据
        SearchHit[] hitsHits = hits.getHits();
        if (hitsHits != null && hitsHits.length != 0){
            // 把hitshits数组转化成Goods集合
            responseVo.setGoodsList(Arrays.stream(hitsHits).map(hitsHit ->{
                // 获取_source json字符串
                String json = hitsHit.getSourceAsString();
                Goods goods = JSON.parseObject(json, Goods.class);
                // 获取高亮的标题,之前goods里面的标题没有高亮，所以需要取高亮的标题替换掉原来没有高亮的标题
                //todo：判空
                Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();
                HighlightField title = highlightFields.get("title");
                goods.setTitle(title.fragments()[0].string());
                return goods;
            }).collect(Collectors.toList()));
        }
        // 2.解析聚合结果集
        Aggregations aggregations = response.getAggregations();
        // 获取品牌id聚合结果集
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> brandIdAggBuckets = brandIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(brandIdAggBuckets)){
            responseVo.setBrands(brandIdAggBuckets.stream().map(bucket->{
                BrandEntity brandEntity = new BrandEntity();
                brandEntity.setId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                // 获取品牌id桶中的子聚合
                Aggregations subAggs = ((Terms.Bucket) bucket).getAggregations();
                // 获取品牌名称对应的子聚合
                ParsedStringTerms brandNameAgg = subAggs.get("brandNameAgg");
                // 通过品牌名称子聚合中的桶中的第一个元素获取品牌名称
                brandEntity.setName(brandNameAgg.getBuckets().get(0).getKeyAsString());
                // 获取logo名称对应的子聚合
                ParsedStringTerms logoAgg = subAggs.get("logoAgg");
                brandEntity.setLogo(logoAgg.getBuckets().get(0).getKeyAsString());
                return brandEntity;
            }).collect(Collectors.toList()));
        }
        //获取分类id聚合结果集
        ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
        List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(categoryIdAggBuckets)){
            responseVo.setCategories(categoryIdAggBuckets.stream().map(bucket ->{
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                ParsedStringTerms categoryNameAgg = ((Terms.Bucket) bucket).getAggregations().get("categoryNameAgg");
                categoryEntity.setName(categoryNameAgg.getBuckets().get(0).getKeyAsString());
                return categoryEntity;
            }).collect(Collectors.toList()));
        }
        //获取规格参数的嵌套聚合结果集
        ParsedNested attrAgg = aggregations.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(attrIdAggBuckets)){
            //将桶集合转化为vo集合
            responseVo.setFilters(attrIdAggBuckets.stream().map(bucket ->{
                SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
                searchResponseAttrVo.setAttrId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                Aggregations subAggs = ((Terms.Bucket) bucket).getAggregations();
                ParsedStringTerms attrNameAgg = subAggs.get("attrNameAgg");
                List<? extends Terms.Bucket> buckets = attrNameAgg.getBuckets();
                searchResponseAttrVo.setAttrName(buckets.get(0).getKeyAsString());
                ParsedStringTerms attrValueAgg = subAggs.get("attrValueAgg");
                List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrValueAggBuckets)){
                    searchResponseAttrVo.setAttrValues(
                            attrValueAggBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                }
                return searchResponseAttrVo;
            }).collect(Collectors.toList()));
        }

        return responseVo;
    }


    private SearchSourceBuilder buildDsl(SearchRequestVo requestVo){
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        String keyWord = requestVo.getKeyWord();
        if (StringUtils.isBlank(keyWord)){
            //todo：打广告，没有条件一般默认页面就是推荐的商品
            throw new RuntimeException("请输入搜索条件！");
        }
        // 1.构建查询和过滤条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQueryBuilder);
        // 1.1.构建匹配查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyWord).operator(Operator.AND));
        // 1.2.构建过滤条件
        // 1.2.1.品牌过滤
        List<Long> brandIds = requestVo.getBrandId();
        if (!CollectionUtils.isEmpty(brandIds)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",brandIds));
        }
        // 1.2.2.分类过滤
        List<Long> categoryIds = requestVo.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryIds)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId",categoryIds));
        }
        // 1.2.3.价格区间
        Double priceFrom = requestVo.getPriceFrom();
        Double priceTo = requestVo.getPriceTo();
        if (priceFrom != null || priceTo != null){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            // 起始价格不为空
            if (priceFrom != null){
                rangeQuery.gte(priceFrom);
            }
            // 终止价格不为空
            if (priceTo != null){
                rangeQuery.lte(priceTo);
            }
            boolQueryBuilder.filter(rangeQuery);
        }
        // 1.2.4.是否有货
        Boolean store = requestVo.getStore();
        // 正常情况下，如果store不为空，就为有货
        if (store != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("store",store));
        }
        // 1.2.5.规格参数的嵌套过滤  ["4:8G-12G", "5:128G-256G"]
        List<String> props = requestVo.getProps();
        if (!CollectionUtils.isEmpty(props)){
            props.forEach(prop->{
                String[] attrs = StringUtils.split(prop, ":");
                if (attrs != null && attrs.length == 2 && StringUtils.isNumeric(attrs[0])){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId",attrs[0]));
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue",StringUtils.split(attrs[1],"-")));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None));
                }
            });
        }
        // 2.构建排序条件 0-得分降序 1-价格降序 2-价格升序 3-销量降序 4-新品降序
        Integer sort = requestVo.getSort();
        switch (sort){
            case 1:
                sourceBuilder.sort("price",SortOrder.DESC);
                break;
            case 2:
                sourceBuilder.sort("price",SortOrder.ASC);
                break;
            case 3:
                sourceBuilder.sort("sales",SortOrder.DESC);
                break;
            case 4:
                sourceBuilder.sort("createTime",SortOrder.DESC);
                break;
            default:
                sourceBuilder.sort("_score",SortOrder.DESC);
        }
        // 3.构建分页
        Integer pageNum = requestVo.getPageNum();
        Integer pageSize = requestVo.getPageSize();
        sourceBuilder.from((pageNum-1)*pageSize);
        sourceBuilder.size(pageSize);
        // 4.构建高亮
        sourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<font style='color:red;'>").postTags("</font>"));
        // 5.构建聚合条件
        // 5.1品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        // 5.2分类聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        // 5.3规格参数聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))
                ));
        // 6.结果集过滤,产品列表只需要显示这几个字段就可以了
        sourceBuilder.fetchSource(new String[]{"defaultImage","skuId","title","subtitle","price"},null);
        System.out.println(sourceBuilder);
        return sourceBuilder;
    }



}
